package com.jintian.smart.kernel.flowable.flowable.listener;

import com.alibaba.dubbo.common.URL;
import com.alibaba.fastjson.JSON;
import com.jintian.smart.kernel.common.util.StringUtil;
import com.jintian.smart.kernel.core.auth.IOrgUnitQuery;
import com.jintian.smart.kernel.core.dto.OrgUnit;
import com.jintian.smart.kernel.core.flow.FlowAction;
import com.jintian.smart.kernel.core.flow.IFlowRuntime;
import com.jintian.smart.kernel.core.flow.ITaskOperation;
import com.jintian.smart.kernel.core.flow.ProcessControl;
import com.jintian.smart.kernel.flowable.component.ProcessContextProvider;
import com.jintian.smart.kernel.flowable.context.DefaultProcessContext;
import com.jintian.smart.kernel.flowable.domain.entity.ActGrant;
import com.jintian.smart.kernel.flowable.mapper.IActGrantMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.PropertyUtils;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent;
import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
import org.flowable.common.engine.api.delegate.event.FlowableEntityEvent;
import org.flowable.common.engine.api.delegate.event.FlowableEvent;
import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener;
import org.flowable.engine.delegate.event.FlowableActivityEvent;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.identitylink.api.IdentityLinkType;
import org.flowable.identitylink.service.impl.persistence.entity.IdentityLinkEntity;
import org.flowable.task.service.HistoricTaskService;
import org.flowable.task.service.impl.persistence.entity.HistoricTaskInstanceEntity;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public class GlobalFlowableEventListener extends AbstractFlowableEngineEventListener implements ApplicationContextAware {
    private static Pattern patten = Pattern.compile("(\\$\\{[^\\$\\{\\}]*\\})+");

    // @Autowired
    private IActGrantMapper mapper;
    // @Autowired
    private IOrgUnitQuery query;

    private ApplicationContext applicationContext;

    public GlobalFlowableEventListener() {
        super(new HashSet<>(Arrays.asList(FlowableEngineEventType.values())));
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    private IActGrantMapper getActGrantMapper() {
        if (this.mapper == null) {
            this.mapper = this.applicationContext.getBean(IActGrantMapper.class);
        }
        return this.mapper;
    }

    public IOrgUnitQuery getOperatorQuery() {
        if (this.query == null) {
            this.query = this.applicationContext.getBean(IOrgUnitQuery.class);
        }
        return this.query;
    }

    @Override
    protected void entityDeleted(FlowableEngineEntityEvent event) {
        if (event.getEntity() instanceof ExecutionEntity) {
            ExecutionEntity executionEntity = (ExecutionEntity) event.getEntity();
            CommandContextUtil.getProcessEngineConfiguration().getVariableServiceConfiguration().getVariableService().deleteVariablesByExecutionId(executionEntity.getId());
        }
    }

    @Override
    protected void entityCreated(FlowableEngineEntityEvent event) {
        if (event.getEntity() instanceof TaskEntity) {
            TaskEntity task = (TaskEntity) event.getEntity();
            ProcessContextProvider.getCurrentProcessContext().addNewTask(task);
        }
    }

    protected String formatFormKey(String template, Map<String, Object> variants) {
        Matcher matcher = patten.matcher(template);
        StringBuffer sb = new StringBuffer();
        int start = 0;
        while (matcher.find()) {
            String group = matcher.group();
            String beanName = group.substring(2, group.indexOf("."));
            String prop = group.substring(group.indexOf(".") + 1, group.length() - 1);
            Object bean = variants.get(beanName);
            try {
                Object value = PropertyUtils.getProperty(bean, prop);
                sb.append(template.substring(start, matcher.start())).append(value);
            } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
                throw new FlowableException("计算formKey异常", e);
            }
            start = matcher.end();
        }
        sb.append(template.substring(start));
        return sb.toString();
    }

    @Override
    protected void processCreated(FlowableEngineEntityEvent event) {
        ExecutionEntity pi = (ExecutionEntity) event.getEntity();
        IFlowRuntime flowRuntime = ProcessContextProvider.getCurrentProcessContext().getFlowRuntime();
        Map<String, Object> processVariables = flowRuntime.getProcessVariables();
        Set<String> exists = pi.getVariableNames();
        if (!exists.contains(FlowAction.VARNAME_START_BIZCLASS)) {
            pi.setVariable(FlowAction.VARNAME_START_BIZCLASS, flowRuntime.getFlow().getBizEntity());
        }
        for (Entry<String, Object> e : processVariables.entrySet()) {
            if (!exists.contains(e.getKey())) {
                pi.setVariable(e.getKey(), e.getValue());
                if (log.isDebugEnabled()) {
                    log.debug("设置流程实例({})变量{}={}", pi.getProcessInstanceId(), e.getKey(), e.getValue());
                }
            }
        }
    }

    @Override
    protected void taskCreated(FlowableEngineEntityEvent event) {
        TaskEntity task = (TaskEntity) event.getEntity();
        String activity = task.getTaskDefinitionKey();
        DefaultProcessContext processContext = ProcessContextProvider.getCurrentProcessContext();
        String key = task.getFormKey();
        if (StringUtil.isNotEmpty(processContext.getTaskId()) && !task.getId().equals(processContext.getTaskId())) {
            task.setVariableLocal("prevTaskId", processContext.getTaskId());
        }
        // 1.修正URL
        if (key == null) {
            key = processContext.getFlowRuntime().getActivityFormUrl(activity);
        }
        if (key != null) {
            key = this.formatFormKey(key, processContext.getTransientVariables());
            URL url = URL.valueOf(key);
            url = url.addParameters(
                    // 业务主键
                    ITaskOperation.FORMURL_PARAM_BUSINESS_KEY, processContext.getBusinessKey(),
                    // 流程id
                    ITaskOperation.FORMURL_PARAM_PROCESSINSTANCEID, task.getProcessInstanceId(),
                    // 任务id
                    ITaskOperation.FORMURL_PARAM_TASKID, task.getId(),
                    // 流程
                    ITaskOperation.FORMURL_PARAM_PROCESS, processContext.getProcess(),
                    // 环节
                    ITaskOperation.FORMURL_PARAM_ACTIVITY, activity,
                    // 环节名称
                    ITaskOperation.FORMURL_PARAM_ACTIVITYLABEL, task.getName());
            task.setFormKey(url.toString());
        }

        // TODO 解决历史任务中参数未解析的问题
        ProcessEngineConfigurationImpl processEngineConfiguration = this.applicationContext.getBean(ProcessEngineConfigurationImpl.class);
        HistoricTaskService historicTaskService = processEngineConfiguration.getTaskServiceConfiguration().getHistoricTaskService();
        HistoricTaskInstanceEntity historicTaskInstance = historicTaskService.recordTaskInfoChange(task, new Date(), processEngineConfiguration);

        // 2. 应用processControl
        this.applyProcessControlToNewTask(task, processContext);
        // 3. 检查MDA配置
        this.applyFlowDBConfigToNewTask(task, processContext);
        // 4. 设置当前执行者为执行人
        long cnt = task.getIdentityLinks().stream().filter(link -> link.getType().equals(IdentityLinkType.CANDIDATE)).count();
        if (cnt == 0 && StringUtil.isAllBlank(task.getAssignee(), task.getOwner())) {
            task.setAssignee(processContext.getOperatorId());
        }
    }

    private void applyFlowDBConfigToNewTask(TaskEntity task, DefaultProcessContext processContext) {
        if (FlowAction.transform.equals(processContext.getAction())) {
            // do nothing
        } else {
            long cnt = task.getIdentityLinks().stream().filter(link -> link.getType().equals(IdentityLinkType.CANDIDATE)).count();
            if (cnt == 0 && StringUtil.isAllBlank(task.getAssignee(), task.getOwner())) {
                // 授权表添加到候选人
                List<ActGrant> list = this.getActGrantMapper().findGrantOrgByFlowCode(processContext.getProcess(), task.getTaskDefinitionKey(), processContext.getProcessVersion());
                if (list.size() == 0) {
                    list = this.getActGrantMapper().findGrantOrgByFlowCode(processContext.getProcess(), task.getTaskDefinitionKey(), null);
                }
                if (list.size() == 0) {
                    list = this.getActGrantMapper().findGrantOrgByFlowCode(processContext.getProcess(), "*", processContext.getProcessVersion());
                }
                if (list.size() == 0) {
                    list = this.getActGrantMapper().findGrantOrgByFlowCode(processContext.getProcess(), "*", null);
                }
                if (list.size() == 1 && OrgUnit.OrgKind.USER.name().equalsIgnoreCase(list.get(0).getOrgKind())) {
                    ActGrant grant = list.get(0);
                    // 唯一且是用户
                    OrgUnit unit = this.getOperatorQuery().query(grant.getOrgKind().toLowerCase(), grant.getOrgCode(), false);
                    if (unit != null) {
                        task.setAssignee(unit.getId());
                    } else {
                        throw new FlowableException("用户" + grant.getOrgCode() + "不存在，请检查环节授权配置表");
                    }

                } else if (list.size() > 1) {
                    for (ActGrant grant : list) {
                        if (OrgUnit.OrgKind.USER.name().equalsIgnoreCase(grant.getOrgKind())) {
                            OrgUnit unit = this.getOperatorQuery().query(grant.getOrgKind().toLowerCase(), grant.getOrgCode(), false);
                            if (unit != null) {
                                task.addCandidateUser(unit.getId());
                            }
                        } else if (OrgUnit.OrgKind.ROLE.name().equalsIgnoreCase(grant.getOrgKind())) {
                            task.addCandidateGroup(grant.getOrgCode());
                        } else {
                            task.addCandidateGroup(grant.getOrgKind().toLowerCase() + ":" + grant.getOrgCode());
                        }
                    }
                }
            }
        }
    }

    private void applyProcessControlToNewTask(TaskEntity task, DefaultProcessContext processContext) {
        Map<String, Object> taskVariables = processContext.getFlowRuntime().getTaskVariables(task.getTaskDefinitionKey());
        ProcessControl processControl = processContext.getProcessControl();
        ProcessControl.ProcessControlItem item = processControl.getFlowTo(task.getTaskDefinitionKey());
        if (item != null) {
            // 设置任务变量
            taskVariables.putAll(item.getVariables());
            if (FlowAction.transform.equals(processContext.getAction())) {
                // do nothing
            } else {
                // 设置任务执行者
                if (item.getExecutors().size() > 0) {
                    List<OrgUnit> executors = item.getExecutors();
                    List<IdentityLinkEntity> defaultLinks = task.getIdentityLinks();
                    for (int i = 0; i < defaultLinks.size(); i++) {
                        IdentityLinkEntity link = defaultLinks.get(i);
                        if (link.getType().equals(IdentityLinkType.CANDIDATE)) {
                            if (StringUtil.isNotBlank(link.getGroupId())) {
                                task.deleteCandidateGroup(link.getGroupId());
                            } else {
                                task.deleteCandidateUser(link.getUserId());
                            }
                        }
                    }
                    if (executors.size() == 1 && executors.get(0).getKind().equals(OrgUnit.OrgKind.USER)) {
                        // 唯一且是用户
                        task.setAssignee(executors.get(0).getId());
                    } else {
                        for (OrgUnit org : executors) {
                            if (org.getKind() == OrgUnit.OrgKind.USER) {
                                task.addCandidateUser(org.getId());
                            } else if (org.getKind().equals(OrgUnit.OrgKind.ROLE)) {
                                task.addCandidateGroup(org.getCode());
                            } else {
                                task.addCandidateGroup(org.getKind().name().toLowerCase() + ":" + org.getCode());
                            }
                        }
                    }
                }
            }
        }
        Set<String> exists = task.getVariableNamesLocal();
        for (Entry<String, Object> e : taskVariables.entrySet()) {
            if (!exists.contains(e.getKey())) {
                task.setVariableLocal(e.getKey(), e.getValue());
                if (log.isDebugEnabled()) {
                    log.debug("设置任务实例({})变量{}={}", task.getId(), e.getKey(), e.getValue());
                }
            }
        }
    }

    @Override
    public final void onEvent(FlowableEvent event) {
        super.onEvent(event);
        if (FlowableEngineEventType.HISTORIC_PROCESS_INSTANCE_ENDED.equals(event.getType())) {
            // TODO 流程结束
            // FlowableEntityEvent entityEvent = (FlowableEntityEvent) event;
            // HistoricProcessInstanceEntity hpi = (HistoricProcessInstanceEntity)
            // entityEvent.getEntity();
            // hpi.setBusinessStatus("finish");
        } else if (FlowableEngineEventType.HISTORIC_ACTIVITY_INSTANCE_ENDED.equals(event.getType())) {
            // 环节实例结束
        }
        if (event instanceof FlowableEntityEvent) {
            FlowableEntityEvent entityEvent = (FlowableEntityEvent) event;
            Object entity = entityEvent.getEntity();
            if (log.isTraceEnabled()) {
                log.trace(event.getType() + " " + entity);
            }
        } else if (event instanceof FlowableActivityEvent) {
            FlowableActivityEvent activityEvent = (FlowableActivityEvent) event;
            if (log.isTraceEnabled()) {
                log.trace(activityEvent.getType() + " " + activityEvent.getActivityId() + "[" + activityEvent.getActivityName() + ":" + activityEvent.getActivityType() + "]");
            }
        } else {
            if (log.isTraceEnabled()) {
                log.trace(event.getType() + "[" + JSON.toJSONString(event) + "]");
            }
        }
    }
}
